using Microsoft.Extensions.Logging;
using Sage.Http.Options;
using Sage.Http.Utils;
using System;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

namespace Sage.Http.Handlers
{
    /// <summary>
    /// 请求限流处理器
    /// </summary>
    public class RateLimitHandler : DelegatingHandler
    {
        private readonly RateLimitOptions _options;
        private readonly ILogger _logger;
        private readonly TokenBucket? _rateLimiter;
        
        /// <summary>
        /// 初始化请求限流处理器
        /// </summary>
        /// <param name="options">限流选项</param>
        /// <param name="logger">日志记录器</param>
        public RateLimitHandler(RateLimitOptions options, ILogger logger)
        {
            _options = options ?? throw new ArgumentNullException(nameof(options));
            _logger = logger ?? throw new ArgumentNullException(nameof(logger));
            
            if (_options.Enabled)
            {
                _rateLimiter = new TokenBucket(
                    capacity: _options.BurstLimit,
                    refillRate: _options.RequestsPerSecond);
            }
        }
        
        /// <summary>
        /// 发送请求，应用限流逻辑
        /// </summary>
        protected override async Task<HttpResponseMessage> SendAsync(
            HttpRequestMessage request, 
            CancellationToken cancellationToken)
        {
            if (!_options.Enabled || _rateLimiter == null)
            {
                return await base.SendAsync(request, cancellationToken);
            }
            
            using var timeoutCts = new CancellationTokenSource(_options.TimeoutMs);
            using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(
                cancellationToken, timeoutCts.Token);
                
            try
            {
                bool acquired = await _rateLimiter.WaitAndTakeAsync(
                    count: 1,
                    timeout: TimeSpan.FromMilliseconds(_options.TimeoutMs),
                    cancellationToken: linkedCts.Token);
                
                if (acquired)
                {
                    return await base.SendAsync(request, cancellationToken);
                }
                else
                {
                    _logger.LogWarning("请求被限流: {RequestUri}", request.RequestUri);
                    throw new HttpRequestException("请求被限流", null, HttpStatusCode.TooManyRequests);
                }
            }
            catch (OperationCanceledException) when (timeoutCts.IsCancellationRequested)
            {
                _logger.LogWarning("请求限流等待超时: {RequestUri}", request.RequestUri);
                throw new HttpRequestException("请求限流等待超时", null, HttpStatusCode.TooManyRequests);
            }
        }
    }
}